﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public class QuinHero:DestructableObject
{
    public int directionVar = 1;
    private Animation run;
    private Animation jump;
    private Animation holdIdle;
    private Animation holdJump;
    private Animation holdRun;
    private Animation heroBurn;
    private Animation heroBurnDead;
    private Animation heroMelt;
    private Animation heroElectric;
    private Vector3 hSpeed = new Vector3(4,0,0);
    private Vector3 dSpeed = new Vector3(0,0,6);
    private Vector3 maxUpSpeed = new Vector3(0,-100,0);
//    private int directionVal = 1;
    private bool facingLeft = false;
    private bool running = false;
    private SpriteEffects effects;
    private bool falling = false;
    private Vector3 aura = new Vector3(50, 50, 50);
    private MissionObject currUseable;
    private bool grabbing = false;
    private bool dead = false;
    private bool hiding = false;
    private bool invisible = false;
    public bool isDead
    {
        get { return dead; }
    }
    private bool deceased = false;

    public QuinHero()
        : base(QuinHero.Type.QuinHero, new Vector3(0,0,0))
    {
        init();
    }

    public QuinHero(QuinHero.Type type, Vector3 placement)
        : base(type, placement)
    {
        init();
    }

    private void init()
    {
        stayActive = true;
        propagateAnimations();
        currentAnimation = stasis;
        showShadow = true;
        physics = false; //Hero needs to control his own physics
    }

    public void setPosition(Vector3 pos)
    {
        origin = pos;
        min = origin - thisType.collisionBoxOriginOffset;
        max = min + thisType.collisionBoxDimentions;
    }

    public void reset(Vector3 positioning)
    {
        health = 1;
        origin = positioning;
        currentAnimation = stasis;
        min = origin - thisType.collisionBoxOriginOffset;
        max = min + thisType.collisionBoxDimentions;
        squashed = false;
        dead = false;
        deceased = false;
        physics = false;
        showShadow = true;
        if (burned)
        {
            burned = false;
            heroBurn.setCurrentFrame(0);
            heroBurn.isFinished = false;
        }
        if(melted)
        {
            melted = false;
            heroMelt.setCurrentFrame(0);
            heroMelt.isFinished = false;
        }
        if (electrocuted)
        {
            electrocuted = false;
            heroElectric.setCurrentFrame(0);
            heroElectric.isFinished = false;
        }
        if (hiding)
        {
            ((UseableObject)currUseable).setFull(false);
            hiding = false;
        }
        if (invisible)
        {
            ((DriveableObject)currUseable).use(false);
            invisible = false;
        }
        if (grabbing)
        {
            currUseable.use(false);
            grabbing = false;
        }
    }

    /**
     * This method creates all the animations used by the hero. It creates an animation object and adds the appropriate
     * frames to the animations using specific assets from the AssetLibrary.
     */
    public void propagateAnimations()
    {
        int i,j;
        // stasis and destroy animations already populated
        // hero run animation.
        run = new Animation("run", 6, true);
        for ( i = 0; i < 4; i++ )
        {
            run.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroRunTest"),
                    new Rectangle(i * 116, 0, 116, 179),
                    0,
                    new Vector2(60, 179)));
        }
        for (i = 0; i < 4; i++)
        {
            run.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroRunTest"),
                    new Rectangle(i * 116, 179, 116, 179),
                    0,
                    new Vector2(60, 179)));
        }
        // hero jump animation
        jump = new Animation("jump", 4, false);
        for (i = 0; i < 4; i++)
        {
            jump.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroJump"),
                    new Rectangle(i * 116, 0, 116, 179),
                    0,
                    new Vector2(60, 179)));
        }
        for (i = 0; i < 3; i++)
        {
            jump.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroJump"),
                    new Rectangle(i * 116, 179, 116, 179),
                    0,
                    new Vector2(60, 179)));
        }
        // hero holding idle animation
        holdIdle = new Animation("holdIdle", 6, true);
        holdIdle.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroHoldingIdle"),
                    new Rectangle(0, 0, 106, 161),
                    0,
                    new Vector2(53, 161)));
        // hero holding jump animation
        holdJump = new Animation("holdJump", 4, false);
        for (i = 0; i < 4; i++)
        {
            holdJump.addFrame(
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroHoldingJump"),
                        new Rectangle(i * 109, 0, 109, 179),
                        0,
                        new Vector2(54, 179)));
        }
        for (i = 0; i < 3; i++)
        {
            holdJump.addFrame(
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroHoldingJump"),
                        new Rectangle(i * 109, 170, 109, 179),
                        0,
                        new Vector2(54, 179)));
        }
        // hero holding run animation
        holdRun = new Animation("holdRun", 6, true);
        for (i = 0; i < 4; i++)
        {
            holdRun.addFrame(
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroHoldingRun"),
                        new Rectangle(i * 120, 0, 120, 179),
                        0,
                        new Vector2(60, 165)));
        }

        for (i = 0; i < 4; i++)
        {
            holdRun.addFrame(
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroHoldingRun"),
                        new Rectangle(i * 120, 179, 120, 179),
                        0,
                        new Vector2(60, 165)));
        }
        // hero burning animation
        heroBurn = new Animation("burning", 4, false);
        for (int k = 0; k < 2; k++)
        {
            for (i = 0; i < 534; i += 267)
            {
                for (j = 0; (i<267 && j < 516) || j < 258; j += 129)
                {
                    heroBurn.addFrame(
                        new Frame(
                            Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroBurning"),
                            new Rectangle(j, i, 129, 267),
                            0,
                            new Vector2(64, 267)));
                }
            }
        }
        heroBurn.addFrame(
            new Frame(
                Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroBurning"),
                new Rectangle(387, 267, 129, 267),
                0,
                new Vector2(64, 267)));
        // hero melting animation
        heroMelt = new Animation("melting", 4, false);
        for (i = 0; i < 370; i += 185)
        {
            for (j = 0; j < 564; j += 141)
            {
                heroMelt.addFrame(
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroMelting"),
                        new Rectangle(j, i, 141, 185),
                        0,
                        new Vector2(70, 160)));
            }
        }
        // hero electrifying animation
        heroElectric = new Animation("electrifying", 4, false);
        for (i = 0; i < 4; i++)
        {
            heroElectric.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroElectrocuting"),
                    new Rectangle(0, 0, 181, 209),
                    0,
                    new Vector2(90, 210))); 
            heroElectric.addFrame(
                 new Frame(
                     Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroElectrocuting"),
                     new Rectangle(181, 0, 181, 209),
                     0,
                     new Vector2(90, 210)));
        }
        //the last frame of the burned hero
        heroBurnDead = new Animation("heroburnDead", 0, true);
        heroBurnDead.addFrame(
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroDeadBurned"),
                    new Rectangle(0, 0, 152, 54),
                    0,
                    new Vector2(76, 54)));
    }

    public override void update(Double timeElapsed)
    {
        // Draw our position on the minimap
        ((LevelBase)Game.getGame().getEnvStack().currentEnv()).getHUD().setPlayerPosition(origin);

        if (!hiding)
        {
            if (!invisible)
            {
                base.update(timeElapsed);
            }
            updateLogic();
            if (!invisible)
            {
                currentAnimation.update(timeElapsed);
                currentAnimation.setEffects(effects);
            }
        }
        checkHiding();
    }

    public override void destroy(CollidableObject cobj)
    {
        if(!(burned || electrocuted || melted || squashed))
        {
            base.destroy(cobj);
            deceased = true;
        }
    }

    public void updateLogic()
    {
        //if my object is destroyed, stop grabbing
        if (grabbing)
        {
            if(currUseable.isCullable)
            {
                grabbing = false;
                if (invisible)
                {
                    invisible = false;
                    currUseable = null;
                }
            }
            else if (deceased || dead || melted || burned || electrocuted)
            {
                grabbing = false;
                currUseable.use(false);
                if (currUseable is LightObject)
                {
                    currUseable.setVelocity(jumpSpeed + new Vector3(-2, 0, 0));
                }
            }
        }
        //if i am burned, start the burning animation, 
        //wait till that is finished and i hit the ground, 
        //then replace myself with a body
        if (burned && currentAnimation.isFinished)
        {
            if (!falling)
            {
                dead = true;
                LevelBase.addSpawnPoint(new SpawnPoint(origin, ViewableObject.Type.BurnedHero));
                return;
            }
            else
            {
                currentAnimation = heroBurnDead;
            }
        }

        if (electrocuted && currentAnimation.isFinished)
        {
            if (!falling)
            {
                dead = true;
                LevelBase.addSpawnPoint(new SpawnPoint(origin, ViewableObject.Type.BurnedHero));
                return;
            }
            else
            {
                currentAnimation = heroBurnDead;
            }
        }

        if (melted && currentAnimation.isFinished)
        {
            dead = true;
            return;
        }
        //if i am squashed, die, fall to the ground, and replace myself with a body
        if (squashed)
        {
            if (!falling)
            {
                dead = true;
                if (deceased)
                {
                    LevelBase.addSpawnPoint(new SpawnPoint(origin, ViewableObject.Type.DeadHero));
                }
                else
                {
                    LevelBase.addSpawnPoint(new SpawnPoint(origin, ViewableObject.Type.FlatHero));
                }
                return;
            }
            else
            {
                currentAnimation = destruction;
                deceased = true;
                //currVelocity = new Vector3(0, 0, 0);
            }
        }
        else if (deceased && !falling)
        {
            dead = true;
            LevelBase.addSpawnPoint(new SpawnPoint(origin, ViewableObject.Type.DeadHero));
            return;
        }
        if(((squashed || deceased) && falling) || electrocuted || melted)
        {
            physics = true;
            checkWalkable();
            setAnimations();
            return;
        }
        running = false;
        Vector3 movement = new Vector3(0, 0, 0);

        if (!deceased)
        {
            if (InputManager.getSingleton().getControlState(InputManager.LEFT_BUTTON) || InputManager.LEFT_ANALOG_LEFT)
            {
                facingLeft = true;
                movement -= hSpeed;
                running = true;
            }
            else if (InputManager.getSingleton().getControlState(InputManager.RIGHT_BUTTON) || InputManager.LEFT_ANALOG_RIGHT)
            {
                facingLeft = false;
                movement += hSpeed;
                running = true;

            }
            if (InputManager.getSingleton().getControlState(InputManager.UP_BUTTON) || InputManager.LEFT_ANALOG_UP)
            {
                movement += dSpeed;
                running = true;

            }
            else if (InputManager.getSingleton().getControlState(InputManager.DOWN_BUTTON) || InputManager.LEFT_ANALOG_DOWN)
            {
                movement -= dSpeed;
                running = true;

            }
        }
        if (!invisible)
        {
            if (falling)//check if i hit the ground
            {
                if (currVelocity.Y < maxDnSpeed.Y)
                {
                    currVelocity += gravity;
                    if (currVelocity.Y > maxDnSpeed.Y)
                    {
                        currVelocity = maxDnSpeed;
                    }
                }
            }
            else if (InputManager.getSingleton().getJustPressedControlState(InputManager.JUMP_BUTTON) &&
                !(currUseable is HeavyObject && grabbing))
            {
                falling = true;
                currVelocity = jumpSpeed;
                if (grabbing)
                {
                    currentAnimation = holdJump;
                }
                else
                {
                    currentAnimation = jump;
                }
                currentAnimation.setCurrentFrame(0);
            }
        }
        if (currUseable != null && InputManager.getSingleton().getJustPressedControlState(InputManager.GRAB_BUTTON))
        {
            if (grabbing)
            {
                if(currUseable is LightObject)
                {
                    Vector3 putDown = canDrop();
                    if(!putDown.Equals(currUseable.position))
                    {
                        currUseable.position = putDown;
                        currUseable.use(false);
                        grabbing = false;
                    }
                }
                else if(currUseable is HeavyObject)
                {
                    currUseable.use(false);
                    grabbing = false;
                }
                else if (currUseable is DriveableObject && canDrag(currUseable) != new Vector3())
                {
                    origin = canDrag(currUseable);
                    min = origin - thisType.collisionBoxOriginOffset;
                    max = min + thisType.collisionBoxDimentions;
                    invisible = false;
                    grabbing = false;
                    currUseable.use(false);
                }
            }
            else
            {
                currUseable.use(true);
                grabbing = true;

                // Check if the object we're grabbing is a current objective.

                if (currUseable is LightObject)
                {
                    currUseable.position = new Vector3(
                        position.X,
                        position.Y - thisType.collisionBoxDimentions.Y,
                        position.Z);
                }
                else if(currUseable is HeavyObject)
                {
                    Vector3 destination = canDrag(currUseable);
                    updateCollisionBoxes(destination - origin);
                    origin = destination;
                    movement = new Vector3(0, 0, 0);
                    currVelocity = new Vector3(0, 0, 0);
                }
                else if (currUseable is DriveableObject)
                {
                    origin = currUseable.position;
                    min = origin - thisType.collisionBoxOriginOffset;
                    max = min + thisType.collisionBoxDimentions;
                    invisible = true;
                    movement = new Vector3(0, 0, 0);
                    currVelocity = new Vector3(0, 0, 0);
                }
                //if i am going to push an object, make sure i have a place to stand
                // ^ i should probably do these checks when looking for the closest object ^
                //pick up /pull the object and set grabbing to true
            }
        }
        if (!invisible && !hiding && InputManager.getSingleton().getJustPressedControlState(InputManager.DYNAMITE_BUTTON))
        {
            Vector3 putDown = canDrop(origin, DynamiteObject.Type.TNT);
            if (!putDown.Equals(origin))
            {
                LevelBase.addObject(new DynamiteObject(DynamiteObject.Type.TNT, putDown));
                movement = new Vector3(0, 0, 0);
            }
        }
        movement += currVelocity;
        move(movement);
        checkWalkable();
        setUseable();
        setAnimations();
    }

    private void checkWalkable()
    {
        List<CollidableObject> floorCheck = LevelBase.collidesWith(
            new Vector3(min.X+1, min.Y, min.Z+1), 
            new Vector3(max.X-1, max.Y, max.Z-1),
            this);
        foreach(CollidableObject co in floorCheck)
        {
            if (co is ViewableObject)
            {
                if (((ViewableObject)co).walkable)
                {
                    falling = false;
                    currVelocity = new Vector3(0, 0, 0);
                    return;
                }
            }
        }
        falling = true;
    }

    private void checkHiding()
    {
        if (hiding && currUseable.isCullable)
        {
            hiding = false;
            currUseable = null;
        }
        if (currUseable is UseableObject && InputManager.getSingleton().getJustPressedControlState(InputManager.USE_BUTTON))
        {
            hiding = !hiding;
            ((UseableObject)currUseable).setFull(hiding);
        }
    }

    /**
     * checks to see if and where the player 
     * can drop the object he is carrying
     */
    private Vector3 canDrop(Vector3 reference, CollidableObject.Type type)
    {
        //i check them against 1 because the player should be the only object returned
        //
        if (facingLeft)
        {
            //check to the left
            if (LevelBase.collidesWith(
                new Vector3(
                    min.X - type.collisionBoxDimentions.X,
                    position.Y - type.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    min.X,
                    position.Y - 1,
                    position.Z + type.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    min.X - type.collisionBoxDimentions.X + type.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }// check to the right
            else if (LevelBase.collidesWith(
                new Vector3(
                    max.X,
                    position.Y - type.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    max.X + type.collisionBoxDimentions.X,
                    position.Y - 1,
                    position.Z + type.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    max.X + type.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }
        }
        else
        {
            // check to the right
            if (LevelBase.collidesWith(
                new Vector3(
                    max.X,
                    position.Y - type.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    max.X + type.collisionBoxDimentions.X,
                    position.Y - 1,
                    position.Z + type.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    max.X + type.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }//check to the left
            else if (LevelBase.collidesWith(
                new Vector3(
                    min.X - type.collisionBoxDimentions.X,
                    position.Y - type.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    min.X,
                    position.Y - 1,
                    position.Z + type.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    min.X - type.collisionBoxDimentions.X + type.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }
        }

        // check to the front
        if (LevelBase.collidesWith(
            new Vector3(
                position.X - type.collisionBoxOriginOffset.X,
                position.Y - type.collisionBoxDimentions.Y,
                position.Z - type.collisionBoxDimentions.Z),
            new Vector3(
                position.X - type.collisionBoxOriginOffset.X + type.collisionBoxDimentions.X,
                position.Y - 1,
                position.Z),
            currUseable).Count == 1)
        {
            return new Vector3(
                position.X,
                position.Y,
                position.Z - type.collisionBoxDimentions.Z);
        }// check to the back
        else if (LevelBase.collidesWith(
                new Vector3(
                    position.X - type.collisionBoxOriginOffset.X,
                    position.Y - type.collisionBoxDimentions.Y,
                    position.Z + thisType.collisionBoxDimentions.Z),
                new Vector3(
                    position.X - type.collisionBoxOriginOffset.X + type.collisionBoxDimentions.X,
                    position.Y - 1,
                    position.Z + thisType.collisionBoxDimentions.Z + type.collisionBoxDimentions.Z),
                currUseable).Count == 1)
        {
            return new Vector3(
                position.X,
                position.Y,
                position.Z + thisType.collisionBoxDimentions.Z);
        }
        return reference;
    }

    /**
     * checks to see if and where the player 
     * can drop the object he is carrying
     */
    private Vector3 canDrop()
    {
        //i check them against 1 because the player should be the only object returned
        //
        if (facingLeft)
        {
            //check to the left
            if (LevelBase.collidesWith(
                new Vector3(
                    min.X - currUseable.myType.collisionBoxDimentions.X,
                    position.Y - currUseable.myType.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    min.X,
                    position.Y - 1,
                    position.Z + currUseable.myType.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    min.X - currUseable.myType.collisionBoxDimentions.X + currUseable.myType.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }// check to the right
            else if (LevelBase.collidesWith(
                new Vector3(
                    max.X,
                    position.Y - currUseable.myType.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    max.X + currUseable.myType.collisionBoxDimentions.X,
                    position.Y - 1,
                    position.Z + currUseable.myType.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    max.X + currUseable.myType.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }
        }
        else
        {
            // check to the right
            if (LevelBase.collidesWith(
                new Vector3(
                    max.X,
                    position.Y - currUseable.myType.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    max.X + currUseable.myType.collisionBoxDimentions.X,
                    position.Y - 1,
                    position.Z + currUseable.myType.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    max.X + currUseable.myType.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }//check to the left
            else if (LevelBase.collidesWith(
                new Vector3(
                    min.X - currUseable.myType.collisionBoxDimentions.X,
                    position.Y - currUseable.myType.collisionBoxDimentions.Y,
                    position.Z),
                new Vector3(
                    min.X,
                    position.Y - 1,
                    position.Z + currUseable.myType.collisionBoxDimentions.Z),
                currUseable).Count == 1)
            {
                return new Vector3(
                    min.X - currUseable.myType.collisionBoxDimentions.X + currUseable.myType.collisionBoxOriginOffset.X,
                    position.Y,
                    position.Z);
            }
        }
            
            // check to the front
        if (LevelBase.collidesWith(
            new Vector3(
                position.X - currUseable.myType.collisionBoxOriginOffset.X,
                position.Y - currUseable.myType.collisionBoxDimentions.Y,
                position.Z - currUseable.myType.collisionBoxDimentions.Z),
            new Vector3(
                position.X - currUseable.myType.collisionBoxOriginOffset.X + currUseable.myType.collisionBoxDimentions.X,
                position.Y - 1,
                position.Z),
            currUseable).Count == 1)
        {
            return new Vector3(
                position.X,
                position.Y,
                position.Z - currUseable.myType.collisionBoxDimentions.Z);
        }// check to the back
        else if (LevelBase.collidesWith(
                new Vector3(
                    position.X - currUseable.myType.collisionBoxOriginOffset.X,
                    position.Y - currUseable.myType.collisionBoxDimentions.Y,
                    position.Z + thisType.collisionBoxDimentions.Z),
                new Vector3(
                    position.X - currUseable.myType.collisionBoxOriginOffset.X + currUseable.myType.collisionBoxDimentions.X,
                    position.Y - 1,
                    position.Z + thisType.collisionBoxDimentions.Z + currUseable.myType.collisionBoxDimentions.Z),
                currUseable).Count == 1)
        {
            return new Vector3(
                position.X,
                position.Y,
                position.Z + thisType.collisionBoxDimentions.Z);
        }
        return currUseable.position;
    }

    private Vector3 canDrag(MissionObject ho)
    {
        bool canStandRight = true;
        Vector3 testStandRight = new Vector3(
            ho.collisionMax.X + thisType.collisionBoxOriginOffset.X,
            ho.position.Y,
            ho.position.Z);
        bool canStandLeft = true;
        Vector3 testStandLeft = new Vector3(
            ho.collisionMin.X-thisType.collisionBoxDimentions.X+thisType.collisionBoxOriginOffset.X,
            ho.position.Y,
            ho.position.Z);
        //test to the left
        List<CollidableObject> stand = LevelBase.collidesWith(
            testStandLeft - thisType.collisionBoxOriginOffset + new Vector3(1, 1, 1), 
            testStandLeft - thisType.collisionBoxOriginOffset + thisType.collisionBoxDimentions + new Vector3(-1, -1, -1), 
            this);
        if (stand.Count > 0)
        {
            canStandLeft = false;
        }
        //test to the right
        stand = LevelBase.collidesWith(
            testStandRight - thisType.collisionBoxOriginOffset + new Vector3(1,1,1),
            testStandRight - thisType.collisionBoxOriginOffset + thisType.collisionBoxDimentions + new Vector3(-1, -1, -1),
            this); 
        if (stand.Count > 0)
        {
            canStandRight = false;
        }
        if (canStandRight && canStandLeft)
        {
            float deltaRight = (origin - testStandRight).Length();
            float deltaLeft = (origin - testStandLeft).Length();
            if (deltaRight < deltaLeft)
            {
                return testStandRight;
            }
            else
            {
                return testStandLeft;
            }
        }
        else if (canStandRight)
        {
            return testStandRight;
        }
        else if (canStandLeft)
        {
            return testStandLeft;
        }
        return new Vector3();
    }
    /**
     * determines and notifies the object that 
     * is closest and can be picked up.
     */
    private void setUseable()
    {
        if (!grabbing)
        {
            List<CollidableObject> useable = LevelBase.collidesWith(min - aura, max + aura, this);
            MissionObject nowUseable = null;
            foreach (CollidableObject mo in useable)
            {
                if (mo is LightObject || mo is DriveableObject || (mo is HeavyObject && !canDrag((HeavyObject)mo).Equals(new Vector3())))
                {
                    if (nowUseable == null)
                    {
                        nowUseable = (MissionObject)mo;
                    }
                    else if ((mo.position - origin).Length() < (nowUseable.position - origin).Length())
                    {
                        nowUseable = (MissionObject)mo;
                    }
                }
            }
            if (nowUseable != currUseable)
            {
                if (nowUseable != null)
                {
                    nowUseable.setUseable(true);
                }
                if (currUseable != null)
                {
                    currUseable.setUseable(false);
                }
                currUseable = nowUseable;
            }
        }
    }

    private void move(Vector3 delta)
    {
        delta = checkCollisions(delta);
        origin += delta;
        if (grabbing)
        {
            currUseable.position += delta;
        }
        updateCollisionBoxes(delta);
    }

    private void updateCollisionBoxes(Vector3 delta)
    {
        min += delta;
        max += delta;
    }

    private Vector3 checkCollisions(Vector3 delta)
    {
        /*
        if (LevelBase.collidesWith(min + delta, max + delta, this).Count > 0)
        {
            return new Vector3(0, 0, 0);
        }
         */
        if (grabbing)
        {
            delta = currUseable.checkCollisions(delta, this);
            if (invisible)
            {
                return delta;
            }
        }
        List<CollidableObject> collisions;// = LevelBase.collidesWith(min + delta, max + delta, this);
        if (delta.X > 0)
        {
            collisions = LevelBase.collidesWith(
                max - new Vector3(0, thisType.collisionBoxDimentions.Y-1, thisType.collisionBoxDimentions.Z-1), 
                max + new Vector3(delta.X, -1, -1), 
                this);
            if (grabbing)
            {
                collisions.Remove(currUseable);
            }
            foreach (CollidableObject c in collisions)
            {
                if (c.collisionMin.X < max.X + delta.X)
                {
                    delta.X = c.collisionMin.X - max.X;
                }
            }
        }
        else if (delta.X < 0)
        {
            collisions = LevelBase.collidesWith(
                min + new Vector3(delta.X, 1,1),
                min + new Vector3(0, thisType.collisionBoxDimentions.Y-1, thisType.collisionBoxDimentions.Z-1),
                this);
            if (grabbing)
            {
                collisions.Remove(currUseable);
            }
            foreach (CollidableObject c in collisions)
            {
                if (c.collisionMax.X > min.X + delta.X)
                {
                    delta.X = c.collisionMax.X - min.X;
                }
            }
        } 
        if (delta.Z > 0)
        {
            collisions = LevelBase.collidesWith(
                max - new Vector3(thisType.collisionBoxDimentions.X-1, thisType.collisionBoxDimentions.Y-1, 0),
                max + new Vector3(-1, -1, delta.Z),
                this);
            if (grabbing)
            {
                collisions.Remove(currUseable);
            }
            foreach (CollidableObject c in collisions)
            {
                if (c.collisionMin.Z < max.Z + delta.Z)
                {
                    delta.Z = c.collisionMin.Z - max.Z;
                }
            }
        }
        else if (delta.Z < 0)
        {
            collisions = LevelBase.collidesWith(
                min + new Vector3(1, 1, delta.Z),
                min + new Vector3(thisType.collisionBoxDimentions.X-1, thisType.collisionBoxDimentions.Y-1, 0),
                this);
            if (grabbing)
            {
                collisions.Remove(currUseable);
            }
            foreach (CollidableObject c in collisions)
            {
                if (c.collisionMax.Z > min.Z + delta.Z)
                {
                    delta.Z = c.collisionMax.Z - min.Z;
                }
            }
        }
        if (delta.Y > 0)
        {
            collisions = LevelBase.collidesWith(
                max - new Vector3(thisType.collisionBoxDimentions.X-1, 0, thisType.collisionBoxDimentions.Z-1),
                max + new Vector3(-1, delta.Y, -1),
                this);
            if (grabbing)
            {
                collisions.Remove(currUseable);
            }
            foreach (CollidableObject c in collisions)
            {
                if (c.collisionMin.Y < max.Y + delta.Y)
                {
                    delta.Y = c.collisionMin.Y - max.Y;
                }
            }
        }
        else if (delta.Y < 0)
        {
            collisions = LevelBase.collidesWith(
                min + new Vector3(1, delta.Y, 1),
                min + new Vector3(thisType.collisionBoxDimentions.X-1, 0, thisType.collisionBoxDimentions.Z-1),
                this);
            if (grabbing)
            {
                collisions.Remove(currUseable);
            }
            foreach (CollidableObject c in collisions)
            {
                if (c.collisionMax.Y > min.Y + delta.Y)
                {
                    delta.Y = c.collisionMax.Y - min.Y;
                }
            }
        }
        return delta;
    }

    private void setAnimations()
    {
        if (facingLeft && !deceased)
        {
            effects = SpriteEffects.FlipHorizontally;
        }
        else
        {
            effects = SpriteEffects.None;
        }
        if (deceased)
        {
            currentAnimation = destruction;
        }
        else if (burned)
        {
            currentAnimation = heroBurn;
        }
        else if (melted)
        {
            currentAnimation = heroMelt;
        }
        else if (electrocuted)
        {
            currentAnimation = heroElectric;
        }
        else
        {
            if (grabbing)
            {
                if (falling)
                {
                    if (currentAnimation != holdJump)
                    {
                        currentAnimation = holdJump;
                        holdJump.setCurrentFrame(holdJump.getTotalFrames() - 1);
                    }
                }
                else if (running)
                {
                    currentAnimation = holdRun;
                }
                else
                {
                    currentAnimation = holdIdle;
                }
            }
            else
            {
                if (falling)
                {
                    if (currentAnimation != jump)
                    {
                        currentAnimation = jump;
                        jump.setCurrentFrame(jump.getTotalFrames() - 1);
                    }
                }
                else if (running)
                {
                    currentAnimation = run;
                }
                else
                {
                    currentAnimation = stasis;
                }
            }
        }
    }

    public override void draw()
    {
        if (!hiding && !invisible)
        {
            base.draw();
        }
    }

    public new class Type : DestructableObject.Type
    {
        public static readonly QuinHero.Type QuinHero = new Type(
            ObjectFactory.TypeEnum.QuinHero,
            new Vector3(50, 133, 0),
            new Vector3(98, 133, 10),
            new ArrayList { 
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroIdleTest"), 
                    new Rectangle(0, 0, 121, 179), 
                    0, 
                    new Vector2(60, 174))},
            new ArrayList { 
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.HERO).get("heroDead"),
                    new Rectangle(0, 0, 165, 55),
                    0,
                    new Vector2(82, 55)) });


        /**
         * Private constructor.
         * This private constructor ensures that only 
         * the pre-specified Type objects can be used 
         * and compared against.
         * @param origin
         * @param collision
         * @param basic
         * @param destruction
         */
        protected Type(ObjectFactory.TypeEnum type, Vector3 origin, Vector3 collision, Boolean walkable,
            ArrayList stasisFrames, String stasisName, int stasisFramerate, Boolean stasisLoopable,
            ArrayList explosionFrames, String explosionName, int explosionFramerate, Boolean explosionLoopable)
            : base(type, origin, collision, walkable, stasisFrames, stasisName, stasisFramerate, stasisLoopable,
            explosionFrames, explosionName,explosionFramerate, explosionLoopable)
        {
            sDepth = .5f;
        }

        protected Type(ObjectFactory.TypeEnum type, Vector3 origin, Vector3 collision,
            ArrayList stasisFrames, ArrayList explosionFrames)
            : base(type, origin, collision, stasisFrames, explosionFrames)
        {
            sDepth = .5f;
        }

        public static new QuinHero.Type getType(ObjectFactory.TypeEnum type)
        {
            switch (type)
            {
                case ObjectFactory.TypeEnum.QuinHero: return QuinHero;
            }
            return null;
        }
    }
}
